<?php

	/** 
	 * MBox importer
	 * 
	 * This class imports any MBOX directory structure supported by the Zend Framework
	 * and uploads it to a Google App Mail account.
	 * 
	 *  @author 	Martijn Dijksterhuis
	 *  @copyright 	2009
	 *  @license 	GPL v2.0 
	 *  @version	$id$
	 */

	require_once 'alreadysentcheck.php';
	require_once 'batchuploader.php';
	
	class MBoxImporter
	{
		// Classes
		private $_googleclient;
		private $_uploader;
		private $_logger; 
				
		// Internal variables
		private $_mailboxpath;
		private $_mailboxfolderset;
		private $_alreadysentchecker;
		private $_includerestricted;
		
		public function __construct()
		{
			$this->_includerestricted = false;
		}
	
		public function setLogger(Zend_Log $log)
		{
			$this->_logger = $log;
		}
				
		public function setBatchUploader(BatchUploader $uploader)
		{
			$this->_uploader = $uploader;
		}
		
		public function setAlreadySentChecker(AlreadySentCheck $check)
		{
			$this->_alreadysentchecker = $check;
		}
		
		public function setIncludeRestrictedFolders($includerestricted)
		{
			if (is_bool($includerestricted))
			 $this->_includerestricted = $includerestricted;
			else
			 throw new Exception('Bool expected');
		}
		
		/**
		 * Thunderbird creates several mail folders which should not normally be uploaded.
		 * 
		 * @param  string $foldername
		 * @return bool   true if the foldername is on the restricted list
		 */
		
		public function isRestrictedFolder($foldername)
		{
			$restricted = array('Trash','Unsent Messages','Drafts');
			
			// Determine if the base folder name matches
			//  /Trash
			// 	/Trash.sbd 
			//	/Trash.sbd/Folder
			
			foreach($restricted as $key => $value)
			 if (strpos($foldername,$value)==1)
			  return true;
			
			return false;
		}

		/**
		 * Opens the mailbox directory
		 * 
		 * @param  string $path directory containing INBOX and sub directories
		 * @throws Zend_Mail_Storage_Exception if an invalid path was passed
		*/
		
		public function setMailBoxPath($path)
		{
			$this->_logger->log('Scanning mailbox folders',Zend_Log::INFO);
			
			// Depending on the Unix/Windows version of Thunderbird the Inbox file is either
			// called "INBOX" or "Inbox"
			
			if (file_exists($path . "/INBOX"))
				$foldername = 'INBOX';
			else if (file_exists($path . "/Inbox"))
				$foldername = 'Inbox';
			else
			{
				$this->_logger->log('Fatal: No Inbox or INBOX file found in Mailbox folder',Zend_Log::EMERG);
				die();
			}
			
			$this->_logger->log("Mailbox folder is called {$foldername}",Zend_Log::DEBUG);			

			// Let Zend_Mail_Storage_Folder_MBox to do a recursive scan of all mailbox folders
			try
			{
				$this->_logger->log('Scanning mailbox folders',Zend_Log::INFO);
				$this->_mailboxpath = $path;
				$this->_mailboxfolderset = new Zend_Mail_Storage_Folder_Mbox(array('dirname' => $path,'folder' => $foldername));
			}
			catch (Zend_Mail_Storage_Exception $e)
			{
				$this->_logger->log('Error: Mailbox dir scan failed',Zend_Log::INFO);
				$this->_logger->log($e->getMessage(),Zend_Log::EMERG);
				die();
			}
		}
		
		public function prettyLabel($mailboxname)
		{
			// remove .SBD and .sbd from the directory
			$dir = array('.sbd','.SBD');
			$pretty = str_replace($dir,'',$mailboxname);

			// Remove Inbox / 
			$pretty = trim(str_ireplace('inbox/','',$pretty));
			
			return $pretty;
		}
		
		public function run()
		{
		   $this->_logger->log('Start processing',Zend_Log::INFO);
			
		   $folders = new RecursiveIteratorIterator($this->_mailboxfolderset->getFolders(),
           			                                RecursiveIteratorIterator::SELF_FIRST);

			foreach ($folders as $localName => $folder) 
			{
				$mailboxname = $this->_mailboxpath .  $folder;
				
				// Skip Trash/Unsent etc 
				if (!$this->_includerestricted && $this->isRestrictedFolder($folder))
				{
					$this->_logger->log("Not uploading {$folder} as it is restricted",Zend_Log::INFO);
				 	continue;
				}
				
				// Sub directories are also returned, skip over them
				if (!is_dir($mailboxname))
				{
					$mailbox = new Zend_Mail_Storage_Mbox(array('filename' => $mailboxname));
					
					// Obtain the number of messages in the mailbox
					$maxMessage = $mailbox->countMessages();
					
					$this->_logger->log("Folder {$folder} contains {$maxMessage} messages",Zend_Log::INFO);
			
					// Retrieve the messages in order
					for($Lp = 1; $Lp <= $maxMessage; $Lp++ )
					{
						$msgid   = $mailboxname . "#" . $Lp;

						// Check if we have already uploaded this message
						if ($this->_alreadysentchecker->check($msgid)) 
						{
						 $this->_logger->log("Skipping $msgid",Zend_Log::INFO);
						 continue;	
						}
						
						// Create a nice label for Google
						$label   = $this->prettyLabel($folder);
					
						// Determine the message size, we skip oversized messages
						$msgSize = $mailbox->getSize($Lp);
						if ($this->_uploader->msgTooLarge($msgSize))
						{
							$this->_logger->log("Warning: Skipping large message {$id} ({$msgsize} byes)",Zend_Log::INFO);				
							continue;
						}

						// Report progress
						$this->_logger->log("Processing {$msgid} ({$msgSize})({$label})",Zend_Log::INFO);						

						// The message 
						$msg = $mailbox->getRawHeader($Lp) .  $mailbox->getRawContent($Lp);
												
						// Batch it
						$this->_uploader->add($msg,$label,$msgid);

						// Free memory immediately
						unset($msg); 

						// A side effect of calling this function is a forced clean up by the GC  
						memory_get_usage(TRUE);
					}
					
					// Free memory immediately 
					unset($mailbox);
				}
			} 

		   // Flush any remaining messages
		   $this->_uploader->flush();
		   $this->_logger->log('Completed run',Zend_Log::INFO);		   
		}

	}
